package org.stephen.recommend.service.impl;

import org.stephen.recommend.enums.LanguageType;
import org.stephen.recommend.common.RedisKeyPrefix;
import org.stephen.recommend.config.PropGetKit;
import org.stephen.recommend.entity.Article;
import org.stephen.recommend.enums.ClassInfo;
import org.stephen.recommend.model.HotNewsList;
import org.stephen.recommend.model.RecommendedArticles;
import org.stephen.recommend.model.ViewedArticles;
import org.stephen.recommend.recommender.ContentBasedRecommender;
import org.stephen.recommend.recommender.HotRecommender;
import org.stephen.recommend.recommender.MahoutUserBasedCollaborativeRecommender;
import org.stephen.recommend.service.CmsArticleService;
import org.stephen.recommend.service.RecommendService;
import org.stephen.recommend.thread.AsyncService;
import org.stephen.recommend.util.CommonUtils;
import org.stephen.recommend.util.RedisUtils;
import org.stephen.recommend.util.SerializeUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;

/**
 * ClassName: RecommendServiceImpl
 * Description:
 * Date: 2020/12/9 11:50
 *
 * @author stephen.qiu
 */
@Service
public class RecommendServiceImpl implements RecommendService {
    private static final Logger logger = LoggerFactory.getLogger(RecommendServiceImpl.class);

    @Autowired
    ContentBasedRecommender contentBasedRecommender;

    @Autowired
    MahoutUserBasedCollaborativeRecommender mahoutUserBasedCollaborativeRecommender;

    @Autowired
    HotRecommender hotRecommender;

    @Autowired
    RedisUtils redisUtils;

    @Autowired
    CmsArticleService cmsArticleService;

    @Autowired
    AsyncService asyncService;

    @Autowired
    PropGetKit propGetKit;

    @Override
    public List<Article> recommendArticles(List<String> blockArticleIds, String userId, LanguageType languageType) {
        if (HotNewsList.getInstance().getArticleList(languageType) == null) {
            return null;
        }
        mahoutUserBasedCollaborativeRecommender.recommend(blockArticleIds, userId, languageType);
        contentBasedRecommender.recommend(blockArticleIds, userId, languageType);
        hotRecommender.recommend(blockArticleIds, userId, languageType);
        List<Article> articles = null;
        if (redisUtils.hasKey(RedisKeyPrefix.RECOMMEND_ARTICLE_IDS + userId)) {
            String recommendArticleIds = redisUtils.get(RedisKeyPrefix.RECOMMEND_ARTICLE_IDS + userId);
            /**
             * 清空等待推荐列表
             */
            redisUtils.delete(RedisKeyPrefix.RECOMMEND_ARTICLE_IDS + userId);
            RecommendedArticles recommendedArticles = (RecommendedArticles) SerializeUtils.stringSerializeObject(redisUtils.get(RedisKeyPrefix.RECOMMENDED_ARTICLE_IDS + userId));
            if (recommendedArticles == null) {
                recommendedArticles = new RecommendedArticles();
            }
            /**
             * 把推荐文章添加到已经推荐文章列表
             */
            List<String> list = Arrays.asList(recommendArticleIds.split(","));
            recommendedArticles.updateRecommendedArticles(list);
            redisUtils.set(RedisKeyPrefix.RECOMMENDED_ARTICLE_IDS + userId, SerializeUtils.serializeObject(recommendedArticles));
            articles = (List<Article>) cmsArticleService.listByIds(list);
        }
        if (articles == null) {
            articles = HotNewsList.getInstance().getRandomArticles(propGetKit.getTotalRecNum(), languageType);
        }
        Map<Article, Double> map = new HashMap<>(CommonUtils.initHashMapSize(articles.size()));
        for (Article article : articles) {
            /**
             * 计算文章得分
             */
            Double points = propGetKit.getInitScore() * (1 + (
                    propGetKit.getReadScore() * article.getReadNumber()
                            + propGetKit.getLikeScore() * article.getLikeNum()
                            + propGetKit.getCommentScore() * article.getCommentNum()))
                    / Double.valueOf(String.format("%.2f",
                    Math.exp(propGetKit.getDeclineFactor() * ChronoUnit.HOURS.between(article.getCreateDate(), LocalDateTime.now()))));
            map.put(article, points);
            logger.info("推荐文章" + article.getTitle() + "||" + ClassInfo.getClassNameById(article.getClassId()));
        }
        List<Map.Entry<Article, Double>> cmsArticleResponseVOs = new ArrayList<>(map.entrySet());
        Collections.sort(cmsArticleResponseVOs, CommonUtils.getComparator());
        return cmsArticleResponseVOs.stream().map(c -> c.getKey()).collect(Collectors.toList());
    }

    @Override
    public void viewArticle(String userId, String articleId, LanguageType languageType) {
        /**
         * 对圈子浏览不做记录
         */
        Article article = cmsArticleService.getById(articleId);
        if (article.getArticleType() != 1) {
            return;
        }
        /**
         * 添加文章到浏览记录
         */
        ViewedArticles viewedArticles;
        if (redisUtils.hasKey(RedisKeyPrefix.VIEW_ARTICLE_IDS + userId)) {
            viewedArticles = (ViewedArticles) SerializeUtils.stringSerializeObject(redisUtils.get(RedisKeyPrefix.VIEW_ARTICLE_IDS + userId));
            viewedArticles.updateViewedArticles(propGetKit.getPreviousDays());
        } else {
            viewedArticles = new ViewedArticles();
        }
        /**
         * 更新浏览列表
         */
        viewedArticles.updateViewedArticles(articleId);
        redisUtils.set(RedisKeyPrefix.VIEW_ARTICLE_IDS + userId, SerializeUtils.serializeObject(viewedArticles));
        asyncService.updateUserTags(articleId, userId, languageType);
        asyncService.findSimilarityUsers(userId, propGetKit.getSimilarityUserNum());
    }

    @Override
    public void deleteArticleById(String articleId, LanguageType languageType) {
        asyncService.deleteArticleById(articleId, languageType);
    }
}
